W3. Divide and Conquer Sorting
1. Theory
1.1 Introduction to Divide and Conquer
Divide and Conquer is a fundamental algorithm design paradigm that solves a problem by breaking it down into smaller, independent subproblems of the same type, solving each subproblem independently, and then combining the solutions to produce the answer to the original problem. This approach is particularly powerful for problems that naturally decompose into independent parts.
The divide-and-conquer strategy is recursive in nature: each recursive call solves a smaller instance of the same problem until reaching a base case (subproblem small enough to solve directly), at which point the algorithm returns and combines results as it unwinds the recursion.
1.1.1 The Three Parts of Divide and Conquer
Every divide-and-conquer algorithm has three essential components:
- Divide: Break the problem into smaller subproblems of the same type, typically reducing the input size. For example, split an array into two halves, or reduce the exponent in exponentiation.
- Conquer: Solve each subproblem recursively. The base case defines when subproblems are small enough to solve directly without further recursion (e.g., an array of one element is already “sorted”).
- Combine: Merge or integrate the solutions of the subproblems to produce the solution to the original problem. This step must be efficient — if combining is expensive, it can dominate the overall running time.
Real-world analogy: Imagine you need to organize a large library. Rather than sorting every book individually, divide the books into smaller sections, sort each section (conquer), then merge the sorted sections back together (combine). The key insight is that merging two sorted lists is much faster than sorting an unsorted one.
1.2 Analyzing Divide-and-Conquer Algorithms: Recurrence Relations
The running time of a divide-and-conquer algorithm is naturally expressed as a recurrence relation — a mathematical equation that defines
A typical recurrence has the form:
: Number of subproblems created in the divide step : Size of each subproblem (the input is divided by ) : Time spent on divide and combine operations (not including the recursive calls)
Example: For merge sort, we create
Solving recurrence relations is crucial because it tells us the overall asymptotic complexity of the algorithm.
1.2.1 Methods for Solving Recurrence Relations
There are three main approaches to finding closed-form solutions for recurrences:
- Substitution method: Guess a bound and prove it correct by mathematical induction. While this works, it requires intuition about what the answer should be.
- Recursion tree method: Visualize the recursion as a tree, sum the work at each level, and use properties of geometric series or other summation techniques. This method provides more intuition about where the time is spent (early vs. late in the recursion).
- Master method: A general recipe for solving many recurrences of the specific form
. This is the most practical method and requires only identifying the parameters , , and .
1.3 The Master Theorem
The Master Theorem (Cormen et al. 2022, Theorem 4.1) provides a direct solution for recurrences of the form:
where
1.3.1 The Three Cases
Case 1 — Recursion dominates: If
Intuition: Most of the work happens in the leaves of the recursion tree. There are
Case 2 — Recursion and divide-combine are balanced: If
Intuition: Work is distributed across all levels of the recursion. If work is distributed evenly, we multiply by the number of levels, which is
Case 3 — Divide-combine dominates: If
Intuition: The work is dominated by the top level of the recursion (the initial divide and combine). The regularity condition ensures that the recurrence remains well-behaved — the cost of combining doesn’t increase by too much when moving up the recursion tree.
What is the “regularity condition”? It says that after dividing the problem into subproblems and combining them at level
1.3.2 Understanding
This quantity represents the total cost of the recursion itself — sum of the work done in all recursive calls (excluding the divide/combine operations). Here’s why:
- There are
subproblems at level 1, each of size . Recursively, they contribute . - At level 0 (the root), there is
problem of size . - At level
, there are problems, each of size . - The recursion has depth
(the size shrinks by factor at each level). - Total number of leaf nodes:
.
1.4 Maximum Subarray Problem
The Maximum Subarray Problem is a classic example of a problem that yields to divide-and-conquer approach and demonstrates how this technique can improve upon naive algorithms.
1.4.1 Problem Definition
Input: A sequence of
Output: Indices
Practical application: This problem arises in financial analysis where you track changes in stock price (profit/loss). A maximum subarray represents the best opportunity to buy and sell a stock. You might also see it in analyzing temperature changes or seismic data — finding when conditions were consistently “good” or “bad.”
Example: For the array
1.4.2 Naive Brute-Force Approach
The straightforward algorithm checks every possible subarray:
for l = 1 to n
for r = l to n
compute sum of A[l...r]
if sum > best_sum:
best_sum = sum
best_l = l
best_r = r
The inner loops check all
1.4.3 Divide-and-Conquer Solution
The key insight is that a maximum subarray in
- Entirely in the left half —
where - Entirely in the right half —
- Crossing the middle — spans from some index in the left half to some index in the right half
For cases 1 and 2, we recurse. For case 3, we need a different approach because no recursion can represent subarrays that cross the division point.
The crossing subarray can be found efficiently: start from the middle and expand left and right, tracking the maximum sum at each step. This is
Algorithm structure:
FIND-MAXIMUM-SUBARRAY(A, l, r):
if l == r:
return (l, r, A[l]) // base case: single element
else:
m = floor((l + r) / 2)
(left_l, left_r, left_sum) = FIND-MAXIMUM-SUBARRAY(A, l, m)
(right_l, right_r, right_sum) = FIND-MAXIMUM-SUBARRAY(A, m + 1, r)
(cross_l, cross_r, cross_sum) = FIND-MAX-CROSSING-SUBARRAY(A, l, m, r)
// Return the maximum of the three cases
if left_sum >= right_sum and left_sum >= cross_sum:
return (left_l, left_r, left_sum)
else if right_sum >= left_sum and right_sum >= cross_sum:
return (right_l, right_r, right_sum)
else:
return (cross_l, cross_r, cross_sum)
The helper function FIND-MAX-CROSSING-SUBARRAY finds the best crossing subarray:
FIND-MAX-CROSSING-SUBARRAY(A, l, m, r):
// Find the best sum starting from m and extending left
left_sum = -∞
sum = 0
for i = m down to l:
sum = sum + A[i]
if sum > left_sum:
left_sum = sum
max_left = i
// Find the best sum starting from m+1 and extending right
right_sum = -∞
sum = 0
for j = m + 1 to r:
sum = sum + A[j]
if sum > right_sum:
right_sum = sum
max_right = j
return (max_left, max_right, left_sum + right_sum)
This helper runs in
1.4.4 Complexity Analysis
Recurrence:
- Divide:
— find the midpoint - Conquer: Two recursive calls, each on
elements - Combine:
— find the crossing subarray
Applying the Master Theorem:
Compute
Since
This is a significant improvement over the
1.5 Merge Sort
Merge Sort is one of the most important sorting algorithms. It is a pure divide-and-conquer algorithm that demonstrates how the paradigm yields efficient, practical solutions.
1.5.1 Overview and Properties
- Type: Divide-and-conquer
- Time complexity:
— guaranteed, both best and worst case - Space complexity:
— requires auxiliary space for merging - Stability: Stable — equal elements maintain their original relative order
- In-place: Not in-place — requires temporary arrays during merging
The stability property is important for sorting objects with multiple fields. For example, if you have employee records sorted by name, then sort by department, a stable sort keeps employees with the same department in name order.
1.5.2 Algorithm Idea
- Divide: Split the array into two halves at the midpoint.
- Conquer: Recursively sort the left and right halves.
- Combine: Merge the two sorted halves into a single sorted array.
Key insight: Merging two already sorted arrays is straightforward and linear in the combined size. This is much faster than sorting an unsorted array.
1.5.3 Merge Operation
Merging two sorted arrays
- Maintain pointers
iandjat the start of and , respectively. - Compare
L[i]andR[j]. Copy the smaller element to and advance the corresponding pointer. - When one array is exhausted, copy the remaining elements from the other array.
Example: Merging
- Compare 1 and 2: copy 1 →
- Compare 4 and 2: copy 2 →
- Compare 4 and 3: copy 3 →
- Compare 4 and 4: copy 4 from
(to maintain stability) → - Continue: →
Important for stability: When comparing equal elements from
Time complexity:
1.5.4 Pseudocode
MERGE-SORT(A, p, r):
if p >= r:
return // zero or one element is already sorted
q = floor((p + r) / 2)
MERGE-SORT(A, p, q) // sort left half
MERGE-SORT(A, q + 1, r) // sort right half
MERGE(A, p, q, r) // merge the two halves
1.5.5 Complexity Analysis
Recurrence:
- Divide:
— computing the midpoint - Conquer: Two recursive calls on arrays of size
- Combine:
— merging takes linear time
Applying the Master Theorem:
Since
Space complexity: We need
1.6 Quicksort
Quicksort is one of the most popular sorting algorithms in practice, despite having a poor worst-case complexity. With proper pivot selection, it achieves excellent average-case performance and minimal space overhead.
1.6.1 Overview and Properties
- Type: Divide-and-conquer
- Time complexity:
worst case, average case - Space complexity:
for the recursion stack - Stability: Not stable — the partitioning process may move equal elements
- In-place: In-place — partitioning modifies the array in place with minimal extra memory
Quicksort’s popularity stems from its excellent average-case performance and minimal space requirements. Most practical implementations use randomized pivot selection to achieve
1.6.2 Algorithm Idea
- Divide: Choose a pivot element and partition the array into three groups:
- Elements less than or equal to the pivot
- The pivot itself
- Elements greater than the pivot
- Conquer: Recursively sort the left partition (elements
pivot) and the right partition (elements pivot). - Combine: Concatenation — since the pivot is already in its final position and both partitions are sorted, the entire array is sorted.
Key insight: Unlike merge sort, the combine step is implicit. No additional work is needed after recursive calls.
1.6.3 Partitioning Algorithm
The partition operation is the core of quicksort. It rearranges the array so that all elements less than or equal to a chosen pivot come before it, and all elements greater come after.
In-place partition algorithm:
PARTITION(A, p, r):
x = A[r] // choose last element as pivot
i = p - 1 // index of the high end of the low partition
for j = p to r - 1: // process each element except the pivot
if A[j] <= x:
i = i + 1
exchange A[i] with A[j] // move element to low partition
exchange A[i + 1] with A[r] // place pivot in its final position
return i + 1 // return pivot's final index
Invariants: At each step:
: elements (low partition) : elements (high partition) : unprocessed : pivot
Time complexity:
1.6.4 Pseudocode
QUICKSORT(A, p, r):
if p < r:
q = PARTITION(A, p, r) // partition and get pivot index
QUICKSORT(A, p, q - 1) // sort elements <= pivot
QUICKSORT(A, q + 1, r) // sort elements > pivot
1.6.5 Worst-Case Complexity
The worst case occurs when the pivot is always the smallest or largest element, resulting in highly unbalanced partitions (one partition has
Recurrence:
Expanding:
This happens when the input is already sorted (or reverse-sorted) and you always pick the first (or last) element as the pivot.
1.6.6 Best-Case Complexity
The best case occurs when the pivot divides the array into two nearly equal halves.
Recurrence:
Applying the Master Theorem:
Since
1.6.7 Average-Case Complexity
With a random pivot (or a good heuristic like “median of three”), the algorithm typically divides the array reasonably well. The analysis shows that even if partitions are not perfectly balanced, the expected time is still
Intuition: Even if one partition has 1/4 of the elements and the other has 3/4, the sum of partition sizes across levels still decreases exponentially, giving logarithmic depth.
Formally (Cormen et al. 2022, §7.4), the expected running time of randomized quicksort is:
1.7 Comparison of Sorting Algorithms
Different sorting algorithms have different trade-offs:
| Algorithm | Best Case | Average Case | Worst Case | Space | Stable | In-place |
|---|---|---|---|---|---|---|
| Insertion Sort | Yes | Yes | ||||
| Merge Sort | Yes | No | ||||
| Quicksort | No | Yes | ||||
| Heapsort | No | Yes |
Selection criteria:
- Merge Sort: Use when worst-case
is required or when stability is essential. Acceptable space overhead. - Quicksort: Use for general-purpose sorting. Excellent average case and minimal space. Avoid on already-sorted or nearly-sorted data without random pivot selection.
- Insertion Sort: Use for small arrays (typically
) or when the array is already nearly sorted. Very efficient in these cases due to low overhead. - Hybrid approach: Real-world implementations often use quicksort for general partitioning but switch to insertion sort for small subarrays (typical threshold:
).
2. Definitions
- Divide-and-Conquer Algorithm: A recursive algorithm that solves a problem by breaking it into smaller independent subproblems of the same type, solving each subproblem recursively (conquer), and combining the solutions.
- Divide: The step where the problem is partitioned into smaller subproblems, typically reducing the input size.
- Conquer: The step where subproblems are solved recursively until they reach a base case (subproblem small enough to solve directly).
- Combine: The step where solutions to subproblems are merged or integrated to produce the solution to the original problem.
- Recurrence Relation: A mathematical equation that expresses the running time
of a recursive algorithm in terms of the running time of smaller instances, used to analyze divide-and-conquer algorithms. - Master Theorem: A direct method for solving recurrence relations of the form
by comparing with . - Case 1 (Master Theorem): When
for some , the recursion dominates and . - Case 2 (Master Theorem): When
for some , divide and recursion are balanced and . - Case 3 (Master Theorem): When
for some and the regularity condition holds, divide-combine dominates and . - Regularity Condition: The condition
(for some ) in Case 3 of the Master Theorem, ensuring that the recurrence is well-behaved and the series of costs converges. - Maximum Subarray Problem: The problem of finding a contiguous subarray with the maximum sum, frequently arising in financial analysis and time-series data.
- Brute Force Approach: An algorithm that tries all possible solutions systematically, often inefficient but straightforward to implement and understand.
- Merge Sort: A stable, divide-and-conquer sorting algorithm that achieves
time in all cases but requires auxiliary space. - Merging: The operation of combining two sorted arrays into a single sorted array in linear time.
- Stability (in sorting): A property where equal elements maintain their original relative order after sorting.
- Quicksort: An in-place, divide-and-conquer sorting algorithm that achieves
average time but worst-case time, with excellent practical performance on random inputs. - Pivot: The element chosen to partition the array in quicksort, around which elements are rearranged into smaller and larger groups.
- Partition: The operation in quicksort that rearranges an array so that elements less than or equal to a pivot precede those greater than the pivot.
- In-place Sorting: A sorting algorithm that sorts the array without requiring additional space proportional to the input size, using only
or extra space.
3. Formulas
- Master Theorem Recurrence Form:
where , , and - Case 1 (Master Theorem): If
for some , then - Case 2 (Master Theorem): If
for some , then - Case 3 (Master Theorem): If
for some and for , then - Cost of Recursion:
represents the total work in all recursive calls across all levels of the recursion tree - Maximum Subarray (Divide-and-Conquer Recurrence):
- Maximum Subarray (Brute Force Complexity):
checking all subarrays - Merge Sort Recurrence:
- Merge Sort Space Complexity:
auxiliary space for merging - Quicksort Best/Average Recurrence:
- Quicksort Worst-Case Recurrence:
- Partition Time Complexity:
for scanning and rearranging elements
4. Practice
4.1. Analyze Time Complexity of Linked List Processing Function (Problem Set 3, Task 1)
Determine the worst-case time complexity of the following function in terms of the input linked list size
/* L is a linked list of size n */
function process(L)
if L.is_empty()
return 0
else if L.size() == 1
return L.head.value
else
lists = new array of 3 empty linked lists
i = 0
A = L.head
while A is not None
if (i mod 4) > 0
lists[i mod 4].add(A.value)
A = A.next
i = i + 1
result = process(lists[1])
result += process(lists[2])
result += process(lists[3])
return result(a) Write down the recurrence relation for
(b) Find the asymptotic complexity
Click to see the solution
Key Concept: Analyze the function’s structure to determine how many elements go into each recursive subproblem, then set up and solve the recurrence.
Part (a): Recurrence Relation
Analyze the function structure:
- Base cases: Empty list and single-element list are handled in
- Recursive case: The function iterates through all
elements once (while loop), distributing them into three sublists - The key question: How many elements go into each sublist?
- Base cases: Empty list and single-element list are handled in
Determine distribution into sublists:
- Elements are added to
lists[i mod 4]only when(i mod 4) > 0 - For indices
: : element is NOT added (skipped) : element added tolists[1] : element added tolists[2] : element added tolists[3]
- Among every 4 consecutive elements, exactly 3 are distributed (indices 1, 2, 3 out of 0, 1, 2, 3)
- In total, approximately
elements are distributed into the three sublists
- Elements are added to
Precise analysis:
- When
(exactly divisible by 4): exactly elements are distributed, so each list has about elements - When
(with ): roughly elements total - In the worst case, the distribution is:
, , elements (approximately equal)
- When
Recurrence relation:
where the
term represents the work done in the while loop (traversing all elements), and represents the three recursive calls on sublists of size approximately each.
Part (b): Asymptotic Complexity Using Master Theorem
Identify parameters:
(three recursive calls) (each subproblem has size approximately ) (work in the non-recursive part, the while loop)
Compute the recursion cost:
More precisely,
Compare
with :- Since
, we have , so dominates
Check Case 3 of Master Theorem:
- We need:
for some - Choose
, so - Then:
✓ - Check regularity condition:
for some - We need:
, which holds with ✓
- We need:
Apply Case 3:
Answer:
(a)
(b) Using the Master Theorem (Case 3):
The function runs in linear time despite the recursive structure, because the
4.2. Solve Recurrence Relations Using Master Method (Problem Set 3, Task 2)
Solve each of the following recurrence relations using the Master Theorem. For each, state whether the master method applies, indicate which case (1, 2, or 3) applies if it does, verify the conditions explicitly, and provide the final answer in
Click to see the solution
(1)
Identify parameters:
, ,
Compute recursion cost:
Note:
So
Compare
with :- We have
, which matches Case 2 with
Apply Case 2 (Cormen et al., Theorem 4.1):
- Condition:
for some - Here:
✓ - Result:
- Condition:
Answer:
(2)
- Check applicability of Master Theorem:
- Standard form:
requires and the subproblem size to be - Here: subproblem size is
, which is NOT of the form (since for any ) - For example, if
, then , which is allowed, but then we have , which means the subproblems grow, not shrink - Master Theorem does not apply because the subproblems grow in size rather than shrink
- Standard form:
- Why Master Theorem fails:
- The recurrence has
instead of with - This violates the standard form of the Master Theorem
- While
, the subproblems do shrink, but we have 5 subproblems of size , not of size . - This is not in the standard form
. The Master Theorem, as stated in Cormen et al., requires the subproblems to be of size exactly .
- The recurrence has
- Conclusion:
- Master Theorem is not applicable to this recurrence.
- To solve it, we would use other techniques (substitution method or recursion tree method), but those are beyond this scope.
Answer: Master method is not applicable. The subproblem size
(3)
Simplify
:- By Stirling’s approximation:
- More precisely:
- So
- By Stirling’s approximation:
Identify parameters:
, ,
Compute recursion cost:
Note:
Compare
with :- We have
, so dominates (even with the log factor in ) - Check: Is
for some ? - For
: - But
, so this doesn’t satisfy Case 1 - Actually, let’s be more careful. We need to check if
for some . vs. : as ,- For any
, eventually dominates multiplied by any constant - So yes,
for small enough (e.g., )
Apply Case 1:
Answer:
(4)
- Check applicability:
- The subproblem size is
, not for integer - Master Theorem does not apply in its standard form
- (The Master Theorem, as stated, requires
with being constant)
- The subproblem size is
- Alternative approach (substitution method):
- Let
, so and - Set
: then becomes - This has the form
with , , - Here
, but is exponential in , not polynomial — the Master Theorem does not apply in the domain either
- Let
- Conclusion:
- Master Theorem is not applicable to the original recurrence.
Answer: Master method is not applicable. The subproblem size
(5)
- Check applicability:
- The recurrence has
, meaning the coefficient of is less than 1 and the subproblem size is (larger than ) - This is NOT in the standard form
with and - Master Theorem does not apply because:
- The coefficient
violates - The subproblem size
means subproblems grow instead of shrink
- The coefficient
- The recurrence has
- Why this doesn’t make sense as a recurrence:
- If subproblems grow in size, the recursion will never reach a base case (for
) - This is not a valid recurrence relation for an algorithm
- If subproblems grow in size, the recursion will never reach a base case (for
- Conclusion:
- Master Theorem is not applicable and the recurrence is malformed.
Answer: Master method is not applicable. The coefficient
4.3. Analyze Modified Merge Sort with Insertion Sort (Problem Set 3, Task 3)
Consider a modification of merge sort that stops recursive division on arrays of size
(a) Write down the pseudocode of the modified sorting algorithm.
(b) What is the time complexity of the modified sorting algorithm in the worst case (in terms of
(c) What is the time complexity of the modified sorting algorithm in the best case (in terms of
Click to see the solution
Part (a): Pseudocode
HYBRID-MERGE-SORT(A, p, r, k):
if r - p + 1 <= k:
// Base case: use insertion sort for small arrays
INSERTION-SORT(A, p, r)
else:
// Recursive case: divide and merge
q = floor((p + r) / 2)
HYBRID-MERGE-SORT(A, p, q, k) // sort left half
HYBRID-MERGE-SORT(A, q + 1, r, k) // sort right half
MERGE(A, p, q, r) // merge the two halves
INSERTION-SORT(A, p, r):
// Standard insertion sort on A[p..r]
for i = p + 1 to r:
key = A[i]
j = i - 1
while j >= p and A[j] > key:
A[j + 1] = A[j]
j = j - 1
A[j + 1] = key
Part (b): Worst-Case Time Complexity
Analyze the recursion structure:
- Arrays larger than size
are recursively divided and merged - Arrays of size
are sorted using insertion sort in time
- Arrays larger than size
Count the number of leaf nodes:
- The recursion tree has nodes corresponding to subarrays of size
- Leaf nodes correspond to subarrays of size
that are sorted with insertion sort - At each level, subarrays are divided in half until size
is reached - Depth of recursion:
levels (dividing by 2 until we reach size ) - Number of leaf nodes (subarrays of size
): approximately
- The recursion tree has nodes corresponding to subarrays of size
Compute total work:
- Insertion sort phase: We have
subarrays of size- Each insertion sort call on a subarray of size
takes in the worst case - Total work in insertion sorts:
- Each insertion sort call on a subarray of size
- Merging phase: At each level
(from bottom up), we merge subarrays- Level just above leaves: merge subarrays of size
into size — total work - Next level up: merge subarrays of size
into size — total work - … continuing up the tree …
- Top level: final merge — total work
- Number of merge levels:
- Total merging work:
- Level just above leaves: merge subarrays of size
- Insertion sort phase: We have
Combine the two phases:
Simplify
, so this is (since the term is absorbed).More carefully:
- If
(constant), then - If
, then (insertion sort dominates) - In general:
- If
Answer for (b):
Part (c): Best-Case Time Complexity
Best case for insertion sort:
- Insertion sort’s best case is
when the array is already sorted - In the best case, each insertion sort call on a subarray of size
takes time
- Insertion sort’s best case is
Compute total work in best case:
- Insertion sort phase:
subarrays, each taking in the best case- Total:
- Total:
- Merging phase:
- In the best case, two sorted arrays of size
merge in time (when elements of one array are all smaller) - Each level of merging still requires comparing and arranging elements, which is
per level - Number of merge levels:
- Total merging work:
- In the best case, two sorted arrays of size
- Insertion sort phase:
Combine the two phases:
This is
, which simplifies to .
Answer for (c):
Summary:
- Worst case:
— dominated by insertion sort if is large - Best case:
— approaches pure merge sort efficiency when input is pre-sorted or is small - Intuition: Setting
to a small constant (like 10) gives practical hybrid sort with complexity and low overhead
4.4. Solve Using Master Theorem (Lecture 3, Example 1)
Solve the recurrence relation and provide the asymptotic bound.
Click to see the solution
Key Concept: Identify parameters
- Identify parameters:
(two recursive calls) (size divided by 4) (constant work for divide and combine)
- Compute the recursion cost:
- Compare
with :- We need to check if
for some - Yes! Choose
. Then , and
- Apply Case 1:
Answer:
4.5. Solve Using Master Theorem (Lecture 3, Example 2)
Click to see the solution
Key Concept: When
- Identify parameters:
- Compute the recursion cost:
- Compare:
- They match! Check if
for some - Yes, with
:
- Apply Case 2:
Answer:
4.6. Solve Using Master Theorem (Lecture 3, Example 3)
Click to see the solution
Key Concept: When
- Identify parameters:
- Compute the recursion cost:
- Compare:
, so dominates. Check if for some- Yes, choose
: ✓
- Verify regularity condition:
- Need:
for some - Choose
✓
- Need:
- Apply Case 3:
Answer:
4.7. Trace Merge Sort Example (Lecture 3, Example 4)
Sort the array
Click to see the solution
Key Concept: Recursively divide the array in half, then merge sorted subarrays.
- Initial array:
- First divide (split in half):
- Left:
- Right:
- Left:
- Recursively sort left
:- Divide:
and - Further divide
: and - Merge
and : - Merge
and :
- Divide:
- Recursively sort right
:- Divide:
and - Further divide
: and - Merge
and : - Merge
and :
- Divide:
- Merge the two sorted halves:
- Left:
, Right: - Compare 2 and 1 → copy 1 →
- Compare 2 and 3 → copy 2 →
- Compare 4 and 3 → copy 3 →
- Compare 4 and 6 → copy 4 →
- Compare 5 and 6 → copy 5 →
- Copy remaining 6 →
- Left:
Answer: Final sorted array:
4.8. Trace Quicksort Partition Example (Lecture 3, Example 5)
Partition the array
Click to see the solution
Key Concept: Rearrange elements so those
- Initial array:
- Pivot
(last element)
- Pivot
- Partitioning process:
- Initialize
(low partition boundary) - Scan
from 0 to 8 (excluding pivot at index 9)
Step Compare Action Array 1 0 4 Skip -1 2 1 9 Skip -1 3 2 5 Skip -1 4 3 1 Swap and0 5 4 4 Skip 0 6 5 7 Skip 0 7 6 3 Skip 0 8 7 6 Skip 0 9 8 8 Skip 0 - Initialize
- Place pivot in its final position:
- Swap
with : Swap (value 9) with pivot 2 - Result:
- Swap
- Interpretation:
- Elements
: Left partition is (at index 0) - Pivot: 2 is now at index 1
- Elements
: Right partition is (indices 2-9)
- Elements
Answer: After partition with pivot 2:
4.9. Find Maximum Subarray Using Divide and Conquer (Lecture 3, Example 6)
Find the maximum subarray of
Click to see the solution
Key Concept: Divide the array, recursively find maximum subarrays in each half, and check for subarrays crossing the middle.
- Initial array:
(indices 1-6) - First divide at midpoint
:- Left:
(indices 1-3) - Right:
(indices 4-6)
- Left:
- Find maximum subarray in left half
:- Divide at
: and - Left
: max subarray is with sum 2 - Right
:- Divide at
(single element): max of and has sum 3- For crossing at
: best from left is , best from right is , sum is - Max of right half:
with sum 3
- Divide at
- Compare: left
(sum 2), right (sum 3), crossing (sum 2) - Max in left half:
with sum 3
- Divide at
- Find maximum subarray in right half
:- Divide at
: and - Left
: sum 4 - Right
:- Further divide:
and - Max of left:
with sum 1 - Max of right:
with sum -5 (better than nothing) - Crossing:
- Max of right half:
with sum 1
- Further divide:
- Compare: left
(sum 4), right (sum 1), crossing (sum 5) - Max in right half:
with sum 5
- Divide at
- Find maximum crossing subarray (at original midpoint
):- Best sum extending left from index 3:
(just index 3) - Best sum extending right from index 4:
(indices 4-5) - Crossing sum:
- Crossing subarray: indices 3-5, which is
- Best sum extending left from index 3:
- Final comparison:
- Left max:
with sum 3 - Right max:
with sum 5 - Crossing max:
with sum 8 - Overall maximum:
with sum 8 (indices 3-5)
- Left max:
Answer: Maximum subarray is
4.10. Analyze Merge Sort Time Complexity (Lecture 3, Example 7)
Given the merge sort recurrence
Click to see the solution
Key Concept: Merge sort is a classic example of Case 2 in the Master Theorem.
- Identify parameters:
(two recursive calls on two halves) (each half is ) (merging takes linear time)
- Compute the recursion cost:
- Compare
with :- They match! Check if
- Yes, with
- Apply Case 2:
- Interpretation:
- Work is distributed across
levels of recursion - Each level does
work (merging all arrays at that level) - Total:
levels × work per level =
- Work is distributed across
Answer: Time complexity of merge sort is
4.11. Analyze Quicksort Worst-Case Time Complexity (Lecture 3, Example 8)
Show that quicksort has worst-case complexity
Click to see the solution
Key Concept: The worst case occurs when the pivot is always at one end, creating unbalanced partitions.
- Worst-case recurrence:
- When the pivot is the smallest (or largest) element, one partition has
elements and the other has 0. - Recurrence:
- When the pivot is the smallest (or largest) element, one partition has
- Expand the recurrence:
- Sum the costs:
- Master Theorem verification:
- Parameters:
, , but this is not in standard form - The Master Theorem doesn’t directly apply
- But expanding the recurrence shows
- Parameters:
Answer: Worst-case time complexity of quicksort is
4.12. Master Theorem Practice Problems (Lecture 3, Task 1)
Solve each recurrence using the Master Theorem. State which case applies and justify.
Click to see the solution
(1)
, ,- Compare
with : , so dominates - Check Case 3:
with ✓ - Regularity:
with ✓ - Answer:
(Case 3)
(2)
, ,- Compare
with : They match! - Check Case 2:
with ✓ - Answer:
(Case 2)
(3)
, ,- Compare
with : dominates - Check Case 3:
for any ✓ - Regularity:
, i.e.,- This holds with
since for ✓
- This holds with
- Answer:
(Case 3)
(4)
, , (since )- Compare
with : dominates (recursion is larger) - Check Case 1:
with ✓ - Answer:
(Case 1)